﻿using Microsoft.Xna.Framework;
using KamRemakeRemake.GameObjects.UnitObjects.ActionObjects;
using System.Collections.Generic;
using System.Linq;
using KamRemakeRemake.Common.Extensions;
using KamRemakeRemake.GameObjects.UnitObjects.ActionObjects.Actions;
using KamRemakeRemake.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace KamRemakeRemake.GameObjects.UnitObjects.Units {
    public abstract class Unit : IActor {
        protected Texture2D texture;

        protected AnimatedSprite currentSprite;

        /// <summary>
        /// Path where all unit sprites are stored
        /// </summary>
        private const string SPRITEPATH = "sprites/units/";

        /// <summary>
        /// contains the base of the sprite, all action suffixes are stored in the action classes
        /// </summary>
        private string _spriteBaseName;
        public string SpriteBaseName {
            get {
                return string.Format("{0}{1}", SPRITEPATH, _spriteBaseName);
            }
            private set { _spriteBaseName = value; }
        }

        /// <summary>
        /// contains the current speed of the unit, 0 for standing still, if moving it contains the unit speed
        /// </summary>
        public double CurrentSpeed { get; set; }

        /// <summary>
        /// Contains the base unitspeed, 0.5 for foot units, 1.0 for horsies, 20 for Galactic Empire walkers
        /// </summary>
        public double UnitSpeed { get; private set; }

        /// <summary>
        /// the current health of the unit
        /// </summary>
        public int Health { get; set; }

        /// <summary>
        /// the original health of the unit, will reset after every fight that is won
        /// </summary>
        public int UnitHealth { get; protected set; }

        /// <summary>
        /// Contains the direction the unit is heading. if null then north
        /// </summary>
        private Direction? _Direction;
        public Direction Direction {
            get { return _Direction.HasValue ? _Direction.Value : Direction.North; }
            set { _Direction = value; }
        }

        /// <summary>
        /// Contains the current position of the unit. to let the unit walk, update this position to 
        /// </summary>
        public Vector2 Position { get; set; }

        public Rectangle Rectangle {
            get {
                return new Rectangle((int)this.Position.X, (int)this.Position.Y, 20, 20);
            }
        }

        /// <summary>
        /// Contains the current action the unit is performing. If no action is loaded the default idle action will be used
        /// </summary>
        private UnitAction _Action;
        public UnitAction Action { get { return _Action ?? new Idle(this); } protected set { _Action = value; } }

        /// <summary>
        /// Get the defaultAction when the unit does nothing or the current action failed to execute
        /// </summary>
        protected UnitAction DefaultAction {
            get { return new Idle(this); }
        }


        /// <summary>
        /// Contains a list of the available actions the unit can do, normally its only walking, dieing and fapping
        /// Actions are only added in this property or the constructor of the unit implementation
        /// </summary>
        protected List<UnitActionSprite> _availableActions;
        public virtual List<UnitActionSprite> AvailableActions { get { return _availableActions; } }

        /// <summary>
        /// Creates the base of a unit. 
        /// </summary>
        /// <param name="spriteBaseName"></param>
        /// <param name="unitSpeed"></param>
        /// <param name="health"></param>
        /// <param name="positionX"></param>
        /// <param name="positionY"></param>
        protected Unit(string spriteBaseName, double unitSpeed, int health, int positionX, int positionY) {
            this.SpriteBaseName = spriteBaseName;
            this.UnitSpeed = unitSpeed;
            this.Health = health;
            this.UnitHealth = health;

            Position = new Vector2(positionX, positionY);
        }

        /// <summary>
        /// do an action for the unit, the action will contain the current unit and process the action.
        /// The action will be reflected against the available actions
        /// </summary>
        /// <param name="action"></param>
        public void DoAction(UnitAction action) {
            if (AvailableActions.Any(x => x.Action == action.ActionType)) {
                Action = action;
            }
        }

        /// <summary>
        /// Execute an action for the specific unit
        /// </summary>
        /// <param name="gameTime"></param>
        public virtual void Update(GameTime gameTime) {
            // When the unit failed to execute the action load the defaultaction and set it to active
            this.Action.Execute();

            // the direction the unit is facing is also the active row from the sprite!!!
            this.currentSprite.ActiveRow = (int)Direction - 1;

            this.currentSprite.Update(gameTime);
        }

        /// <summary>
        /// Draw the sprite. also checks if the spritename is still relevant i.a. to the loaded action, if not load the new texture in the contentmanager
        /// </summary>
        /// <param name="gameTime"></param>
        /// <param name="spriteBatch"></param>
        /// <param name="contentManager"></param>
        public virtual void Draw(GameTime gameTime, SpriteBatch spriteBatch, ContentManager contentManager) {
            if (currentSprite.Texture.Name != this.GetSpriteName(Action.ActionType.ToString())) {
                LoadContent(contentManager);
            }

            this.currentSprite.Draw(spriteBatch, Position);
        }

        /// <summary>
        /// load the content for the current action and unit
        /// </summary>
        /// <param name="contentManager"></param>
        public virtual void LoadContent(ContentManager contentManager) {
            this.texture = contentManager.Load<Texture2D>(this.GetSpriteName(Action.ActionType.ToString()));

            if (this.Action != null) {
                var unitActionSprite = AvailableActions.FirstOrDefault(x => x.Action == this.Action.ActionType);

                if (unitActionSprite != null) {
                    this.currentSprite = new AnimatedSprite(this.texture, unitActionSprite.Rows,
                                                            unitActionSprite.Columns);
                }
            }
        }

        public string GetSpriteName(string suffix) {
            var returnString = string.Format("{0}/", this.SpriteBaseName);

            if (suffix.HasValue()) {
                returnString = string.Format("{0}{1}", returnString, suffix);
            }

            return returnString;
        }
    }
}
